/*------------------------------------------------------------------------------
This file is part of MUTS.

    MUTS is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    MUTS is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
------------------------------------------------------------------------------*/

#include "stdafx.h"
#include "Win32FFMpegStreaming.h"

HANDLE hChildProcess = NULL;
HANDLE hStdIn = NULL; // Handle to parents std input.
BOOL bRunThread = TRUE;

wchar_t *g_strParams;
SOCKET *g_typClientSocket;

//--------------------------------------------------------------------------------
// Author : Benjamin BALET
// Date : 2010-04-03
// Description : 
// Parameters :
//		
//							
// Return : Result of the thread
// Changes : yyyy-mm-dd - description of change
//--------------------------------------------------------------------------------
void win32_stream(wchar_t *p_strParams, SOCKET *l_typClientSocket)
{
  HANDLE hOutputReadTmp,hOutputRead,hOutputWrite;
  HANDLE hInputWriteTmp,hInputRead,hInputWrite;
  HANDLE hErrorWrite;
  HANDLE hThread;
  DWORD ThreadId;
  SECURITY_ATTRIBUTES sa;

  g_strParams = p_strParams;
  g_typClientSocket = l_typClientSocket;

  // Set up the security attributes struct.
  sa.nLength= sizeof(SECURITY_ATTRIBUTES);
  sa.lpSecurityDescriptor = NULL;
  sa.bInheritHandle = TRUE;

  // Create the child output pipe.
  if (!CreatePipe(&hOutputReadTmp,&hOutputWrite,&sa,0))
     DisplayError("CreatePipe");

  // Create a duplicate of the output write handle for the std error
  // write handle. This is necessary in case the child application
  // closes one of its std output handles.
  if (!DuplicateHandle(GetCurrentProcess(),hOutputWrite,
                       GetCurrentProcess(),&hErrorWrite,0,
                       TRUE,DUPLICATE_SAME_ACCESS))
     DisplayError("DuplicateHandle");

  // Create the child input pipe.
  if (!CreatePipe(&hInputRead,&hInputWriteTmp,&sa,0))
     DisplayError("CreatePipe");

  // Create new output read handle and the input write handles. Set
  // the Properties to FALSE. Otherwise, the child inherits the
  // properties and, as a result, non-closeable handles to the pipes
  // are created.
  if (!DuplicateHandle(GetCurrentProcess(),hOutputReadTmp,
                       GetCurrentProcess(),
                       &hOutputRead, // Address of new handle.
                       0,FALSE, // Make it uninheritable.
                       DUPLICATE_SAME_ACCESS))
     DisplayError("DupliateHandle");

  if (!DuplicateHandle(GetCurrentProcess(),hInputWriteTmp,
                       GetCurrentProcess(),
                       &hInputWrite, // Address of new handle.
                       0,FALSE, // Make it uninheritable.
                       DUPLICATE_SAME_ACCESS))
  DisplayError("DupliateHandle");


  // Close inheritable copies of the handles you do not want to be
  // inherited.
  if (!CloseHandle(hOutputReadTmp)) DisplayError("CloseHandle");
  if (!CloseHandle(hInputWriteTmp)) DisplayError("CloseHandle");


  // Get std input handle so you can close it and force the ReadFile to
  // fail when you want the input thread to exit.
  if ( (hStdIn = GetStdHandle(STD_INPUT_HANDLE)) ==
                                            INVALID_HANDLE_VALUE )
     DisplayError("GetStdHandle");

  PrepAndLaunchRedirectedChild(hOutputWrite, hInputRead, hErrorWrite);

  // Close pipe handles (do not continue to modify the parent).
  // You need to make sure that no handles to the write end of the
  // output pipe are maintained in this process or else the pipe will
  // not close when the child process exits and the ReadFile will hang.
  if (!CloseHandle(hOutputWrite)) DisplayError("CloseHandle");
  if (!CloseHandle(hInputRead )) DisplayError("CloseHandle");
  if (!CloseHandle(hErrorWrite)) DisplayError("CloseHandle");

  // Launch the thread that gets the input and sends it to the child.
  hThread = CreateThread(NULL,0,GetAndSendInputThread,
                          (LPVOID)hInputWrite,0,&ThreadId);
  if (hThread == NULL) DisplayError("CreateThread");

  // Read the child's output.
  ReadAndHandleOutput(hOutputRead);
  // Redirection is complete

  // Force the read on the input to return by closing the stdin handle.
  if (!CloseHandle(hStdIn)) DisplayError("CloseHandle");

  // Tell the thread to exit and wait for thread to die.
  bRunThread = FALSE;

  if (WaitForSingleObject(hThread,INFINITE) == WAIT_FAILED)
     DisplayError("WaitForSingleObject");

  if (!CloseHandle(hOutputRead)) DisplayError("CloseHandle");
  if (!CloseHandle(hInputWrite)) DisplayError("CloseHandle");
}


//--------------------------------------------------------------------------------
// Author : Benjamin BALET
// Date : 2010-04-03
// Description : Sets up STARTUPINFO structure, and launches redirected child.
// Parameters :
//		
//							
// Return : Result of the thread
// Changes : yyyy-mm-dd - description of change
//--------------------------------------------------------------------------------
void PrepAndLaunchRedirectedChild(HANDLE hChildStdOut,
                                 HANDLE hChildStdIn,
                                 HANDLE hChildStdErr)
{
  PROCESS_INFORMATION pi;
  STARTUPINFO si;

  // Set up the start up info struct.
  ZeroMemory(&si,sizeof(STARTUPINFO));
  si.cb = sizeof(STARTUPINFO);
  si.dwFlags = STARTF_USESTDHANDLES;
  si.hStdOutput = hChildStdOut;
  si.hStdInput  = hChildStdIn;
  si.hStdError  = hChildStdErr;
   // Use this if you want to hide the child:
  si.wShowWindow = SW_HIDE;


  // Launch the process that you want to redirect
  if (!CreateProcess(NULL, (LPWSTR) g_strParams, NULL, NULL, TRUE,
                     CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi))
     DisplayError("CreateProcess");

  // Set global child process handle to cause threads to exit.
  hChildProcess = pi.hProcess;

  // Close any unnecessary handles.
  if (!CloseHandle(pi.hThread)) DisplayError("CloseHandle");
}

//--------------------------------------------------------------------------------
// Author : Benjamin BALET
// Date : 2010-04-03
// Description : Monitors handle for input. Exits when child exits or pipe breaks.
// Parameters :
//		
//							
// Return : Result of the thread
// Changes : yyyy-mm-dd - description of change
//--------------------------------------------------------------------------------
void ReadAndHandleOutput(HANDLE hPipeRead)
{
  CHAR lpBuffer[MAX_BUFLEN];
  DWORD nBytesRead;
  int l_intSendResult;

  while(TRUE)
  {
     if (!ReadFile(hPipeRead, lpBuffer, sizeof(lpBuffer), &nBytesRead,NULL) || !nBytesRead)
     {
        if (GetLastError() == ERROR_BROKEN_PIPE)
           break; // pipe done - normal exit path.
        else
           DisplayError("ReadFile"); // Something bad happened.
     }

	l_intSendResult = send(*g_typClientSocket, lpBuffer, nBytesRead, 0);
	if (l_intSendResult == SOCKET_ERROR) {
		printf("send failed: %d\n", WSAGetLastError());
		closesocket(*g_typClientSocket);
		WSACleanup();
		DisplayError("Problem with socket while streaming"); // Something bad happened.
		break;
	}
  }
}

//--------------------------------------------------------------------------------
// Author : Benjamin BALET
// Date : 2010-04-03
// Description : Thread procedure that monitors the console for input and sends
// input to the child process through the input pipe.
// This thread ends when the child application exits.
// Parameters :
//		
//							
// Return : Result of the thread
// Changes : yyyy-mm-dd - description of change
//--------------------------------------------------------------------------------
DWORD WINAPI GetAndSendInputThread(LPVOID lpvThreadParam)
{
  CHAR read_buff[MAX_BUFLEN];
  DWORD nBytesRead, nBytesWrote;
  HANDLE hPipeWrite = (HANDLE)lpvThreadParam;

  // Get input from our console and send it to child through the pipe.
  while (bRunThread)
  {
	 if (!ReadFile(hStdIn, read_buff, MAX_BUFLEN, &nBytesRead, NULL))
		 break;	//End of the reading

     if (!WriteFile(hPipeWrite, read_buff, nBytesRead, &nBytesWrote, NULL))
     {
        if (GetLastError() == ERROR_NO_DATA)
           break; // Pipe was closed (normal exit path).
        else
        DisplayError("WriteFile");
     }
  }

  return 1;
}

//--------------------------------------------------------------------------------
// Author : Benjamin BALET
// Date : 2010-04-03
// Description : Displays the error number and corresponding message.
// Parameters :
//		
//							
// Return : Result of the thread
// Changes : yyyy-mm-dd - description of change
//--------------------------------------------------------------------------------
void DisplayError(char *pszAPI)
{
   LPVOID lpvMessageBuffer;

   FormatMessage(
            FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
            NULL, GetLastError(),
            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
            (LPTSTR)&lpvMessageBuffer, 0, NULL);

     printf("ERROR: API    = %s.\n   error code = %d.\n   message    = %s.\n",
            pszAPI, GetLastError(), (char *)lpvMessageBuffer);

   LocalFree(lpvMessageBuffer);
}
